package com.miyake.dsp.osa.lab.wrapper;

import com.miyake.dsp.ComplexArray;
import com.miyake.dsp.common.CommonTool;
import com.miyake.dsp.filter.FilterImplScilabProxy;
import com.miyake.dsp.filter.FilterSetting;
import com.miyake.dsp.filter.FilterSetting.FilterAlgo;
import com.miyake.dsp.filter.FilterSetting.FilterType;
import com.miyake.dsp.generator.PulseGenerator;
import com.miyake.dsp.generator.PulseSetting;
import com.miyake.dsp.osa.lab.Calculator;
import com.miyake.dsp.osa.lab.Convolution2;
import com.miyake.dsp.osa.lab.LabUiAnnotation;
import com.miyake.dsp.otdr.Otdr;
import com.miyake.dsp.otdr.OtdrImpulseResponseSetting;
import com.miyake.dsp.otdr.OtdrSetting;
@LabUiAnnotation(caption="OTDR Simulator (Tx)")
public class OtdrSimulatorTxWrapper extends Calculator {
	public static final String IN_DISTANCE = "Distance Range";
	public static final String IN_PULSE = "Pulse Width";
	public static final String OUT_LOSS = "Loss";
	public static final String OUT_IMPULSERES = "Impulse Response";
	public static final String OUT_DISTANCE = "Distance";
	public static final String OUT_PULSE = "Pulse Waveform";
	public static final String OUT_FREQUENCY = "Sampling Frequency";
	private OtdrSetting setting;

	public OtdrSimulatorTxWrapper(OtdrSetting setting) {
		this.setting = setting;
		initInput(IN_DISTANCE);
		initInput(IN_PULSE);
		initOutput(OUT_LOSS);
		initOutput(OUT_DISTANCE);
		initOutput(OUT_PULSE);
		initOutput(OUT_FREQUENCY);
		initOutput(OUT_IMPULSERES);
	}
	@Override
	public synchronized void process() {		
		// Pulse
		double pulseWidth;
		if (getInput(IN_PULSE) != null) {
			pulseWidth = getInput(IN_PULSE).getRealPart()[0];
		}
		else {
			pulseWidth = setting.getPulseWidth();
		}

		double res = 1.0 / setting.getSamplingFrequency() * 3e8 /1.5/2.0; // [m]
		PulseGenerator pulseProcessor = new PulseGenerator(
				new PulseSetting(
						pulseWidth * 1e-9*3e8/1.5/2.0, 
						setting.getPulseLevel(), 50, 
						1/res/*setting.getSamplingFrequency()*/
				)
		);
		
		double[] pulse = pulseProcessor.getProcessed().getRealPart();
		
		// Filter
		FilterSetting filterSetting1 = new FilterSetting(
				FilterAlgo.Butterworth, 
				FilterType.Lowpass, 1, 
				setting.getSamplingFrequency()/3);
		filterSetting1.setFrequency(setting.getSamplingFrequency());
		FilterImplScilabProxy filter1 = new FilterImplScilabProxy(filterSetting1);
		filter1.setSource(new ComplexArray(pulse));
		double[] filteredPulse = filter1.getProcessed().getRealPart();
		double[] powPulse = CommonTool.toPower(filteredPulse, 1.0);
		setOutput(OUT_PULSE, powPulse);
		
		// OTDR impulse response
		double distanceRange;
		if (getInput(IN_DISTANCE) != null) {
			distanceRange = getInput(IN_DISTANCE).getRealPart()[0];
		}
		else {
			distanceRange = setting.getDistanceRange();
		}
		OtdrImpulseResponseSetting otdrIRsetting = new OtdrImpulseResponseSetting(setting.getSamplingFrequency(), setting.getWavelength(), distanceRange);
		otdrIRsetting.setEntryPoint(setting.getEntryPoint());
		Otdr otdrProcessor = new Otdr(otdrIRsetting);
		double[] otdrImpulse = otdrProcessor.getProcessed().getRealPart();
		setOutput(OUT_IMPULSERES, otdrImpulse);
		
		// Convolution
		double[] trace = new Convolution2().getResult(powPulse, otdrImpulse);

		/*
		// Noise
		NoiseGenerator noiseProcessor = new NoiseGenerator(new NoiseSetting(setting.getReceiverNoise()));
		noiseProcessor.setSource(new ComplexArray(trace));
		ComplexArray traceWithNoise = noiseProcessor.getProcessed();
		
		// Filter
		FilterSetting filterSetting2 = new FilterSetting(
				FilterAlgo.Butterworth, 
				FilterType.Lowpass, 1, 
				setting.getReceiverBandwidth());
		filterSetting2.setFrequency(setting.getSamplingFrequency());
		FilterImplScilabProxy filter2 = new FilterImplScilabProxy(filterSetting2);
		filter2.setSource(traceWithNoise);
		
		double[] filteredTrace = filter2.getProcessed().getRealPart();
		*/
		setOutput(OUT_LOSS, trace);
		setOutput(OUT_DISTANCE, getDistance(trace, distanceRange));
		setOutput(OUT_FREQUENCY, setting.getSamplingFrequency());
		
	}
	private double[] getDistance(double[] filteredTrace, double range) {
		int size = filteredTrace.length;
		double step = range / (double)size;
		double[] ret = new double[size];
		for (int i = 0; i < size; i++) {
			ret[i] = step * (double)i;
		}
		return ret;
	}

	@Override
	public Object getSetting() {
		return this.setting;
	}

}
